#ifndef __M_SERVICE_H__
#define __M_SERVICE_H__

#include "User.hpp"
#include "Util.hpp"
#include "config.hpp"
#include "data.hpp"
#include "../Referenced_Lib/cpp-httplib/httplib.h"

/*
    1.提供上传接口
    2.提供下载接口
    3.提供显示接口
*/

extern std::unordered_map<std::string, std::shared_ptr<cloud::DataManager>> _Data;

namespace cloud
{
    class service
    {
    private:
        int _server_port;
        std::string _server_ip;
        std::string _download_prefix;
        httplib::Server _server;

    private:
        static void Upload(const httplib::Request &req, httplib::Response &rep) // 上传文件
        {
            auto ret = req.has_file("file"); // 判断有没有文件上传的区域
            if (ret == false)
            {
                rep.status = 400;
                return;
            }

            std::string cookie = req.get_header_value("Cookie");
            std::cout << cookie << std::endl;
            size_t CookiePos = cookie.find("username");
            size_t CookieBegin = CookiePos + 9;
            size_t CookieEnd = cookie.find(",", CookieBegin);
            std::string username = cookie.substr(CookieBegin, CookieEnd - CookieBegin);
            // 获取邮箱地址

            std::string Email = UserManager::GetInstance()->GetEmail(username);
            // 通过邮箱地址获取对应的data
            std::shared_ptr<cloud::DataManager> _data = _Data[Email];

            const auto &file = req.get_file_value("file"); // 获取文件
            // file.filename 文件名称  file.content 文件内容
            std::string back_dictory = config::GetInstance()->GetBackDictory() + Email + "/"; // 获取文件名
            std::string real_path = back_dictory + FileUtil(file.filename).FileName();        // 注意file的filename是带原本目录的文件名
            FileUtil fi(real_path);
            fi.SetContent(file.content); // 获取文件内容
            backupInfo info;             // 设置备份信息
            info.NewBackupInfo(real_path, username);
            _data->Insert(info);
            rep.status = 301;
            std::string url = "http://47.115.227.93:8080/Upload.html?提交成功";
            rep.set_header("Location", url.c_str());

            std::cout << file.filename << " upload success" << std::endl;
            return;
        }
        static std::string GetEtag(backupInfo &info) //"文件名-大小-最后修改时间",获取该标签,用来判断文件是否被修改
        {
            FileUtil file(info._real_path);
            std::stringstream ss;
            ss << file.FileName();
            ss << "-";
            ss << file.FileSize();
            ss << "-";
            ss << file.FileLastMTime();
            return ss.str();
        }
        static void Download(const httplib::Request &req, httplib::Response &rep)
        {
            std::string cookie = req.get_header_value("Cookie");
            size_t CookiePos = cookie.find("username");
            size_t CookieBegin = CookiePos + 9;
            size_t CookieEnd = cookie.find(",", CookieBegin);
            std::string username = cookie.substr(CookieBegin, CookieEnd - CookieBegin);

            // 获取邮箱地址
            std::string Email = UserManager::GetInstance()->GetEmail(username);
            // 通过邮箱地址获取对应的data
            std::shared_ptr<cloud::DataManager> _data = _Data[Email];

            // 1.获得客户端请求资源路径
            // 2.获取备份信息
            backupInfo info;
            _data->GetOneByUrl(req.path, &info);
            // 3.判断是否被压缩,如果被压缩就要进行解压
            if (info._pack_flag == true)
            {
                FileUtil file(info._pack_path);
                file.uncompress(info._real_path);
                info._pack_flag = false;
                file.Remove();
                // _data->Update(info);
            }
            // 4.获取Etag标签,判断是否下载一半被中断,是否采用断点续传,而不是重新下载
            bool retrans = false;
            std::string Old_Etag;
            if (req.has_header("If_Range") == true)
            {
                Old_Etag = req.get_header_value("If_Range");
                // 有If-Range字段且，这个字段的值与请求文件的最新etag一致则符合断点续传,不一样证明文件已经修改
                if (GetEtag(info) == Old_Etag)
                {
                    retrans = true;
                }
            }
            // 5.进行文件传输
            FileUtil file(info._real_path);
            if (retrans == false) // 不是断点续传
            {
                file.GetContent(&rep.body);
                rep.set_header("Accept-Ranges", "byte");
                rep.set_header("Etag", GetEtag(info));
                rep.set_header("Content-Type", "application/octet-stream");
                rep.status = 200;
            }
            else // 断点续传
            {
                // httplib内部实现了对于区间请求也就是断点续传请求的处理
                // 只需要我们用户将文件所有数据读取到rsp.body中，它内部会自动根据请求
                // 区间，从body中取出指定区间数据进行响应
                // std::string  range = req.get_header_val("Range"); bytes=start-end
                file.GetContent(&rep.body);
                rep.set_header("Accept-Ranges", "byte");
                rep.set_header("Etag", GetEtag(info));
                rep.set_header("Content-Type", "application/octet-stream");
                rep.status = 204; // 区间请求响应的是204
            }
        }

        static std::string TimeToString(const time_t &t)
        {
            std::string TimeString = std::ctime(&t);
            return TimeString;
        }

        static void ListShow(const httplib::Request &req, httplib::Response &rep) // 显示已经备份的文件
        {
            // 需要的前端的页面
            /*
                <html>
                <head>
                    <title>Download</title>
                    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />//设置utf-8编码格式
                </head>
                <body>
                    <h1>Download</h1>
                    <table>
                        <tr>
                            <td><a href="下载的url">文件名</a></td>
                            <td align="right">  文件最后修改的时间 </td>
                            <td align="right">  大小 </td>
                        </tr>
                    </table>
                </body>
            </html>
            */

            // 获取cookie里面的用户
            std::string cookie = req.get_header_value("Cookie");
            size_t CookiePos = cookie.find("username");
            size_t CookieBegin = CookiePos + 9;
            size_t CookieEnd = cookie.find(",", CookieBegin); // 由于某种原因，Cookie: username=戒酒的李白%28，只取到username=戒酒的李白(
            std::string username = cookie.substr(CookieBegin, CookieEnd - CookieBegin);
            // 获取邮箱地址
            std::string Email = UserManager::GetInstance()->GetEmail(username);
            // 通过邮箱地址获取对应的data
            std::shared_ptr<cloud::DataManager> _data = _Data[Email];

            std::vector<backupInfo> array;
            _data->GetAll(array);
            std::stringstream ss;
            ss << "<html><head><title>Download</title><meta http-equiv='Content-Type' content='text/html; charset=utf-8' /></head><body><h1>Download</h1><table>";
            for (auto &e : array)
            {
                std::string filename = FileUtil(e._real_path).FileName();
                ss << "<tr><td><a href=" << e.url << ">" << filename << "</a></td>";
                ss << "<td align='right'>" << TimeToString(e._mtime) << "</td>";
                ss << "<td align='right'>" << e._fsize / 1024 << "k</td>";
            }
            ss << "</tr></table></body></html>";
            rep.body = ss.str();
            rep.set_header("Content-Type", "text/html"); // 内容的类型是html
            rep.status = 200;
            return;
        }

        static void HomePage(const httplib::Request &req, httplib::Response &rep)
        {
            FileUtil file("./Page_Image/HomePage.html");
            std::string content;
            file.GetContent(&content);
            rep.body = content;
            rep.set_header("Content-Type", "text/html");
        }

        // 登出操作
        static void Logout(const httplib::Request &req, httplib::Response &rep)
        {
            rep.status = 301;
            std::string url = "http://47.115.227.93:8080/HomePage.html?Logout";
            rep.set_header("Location", url.c_str());
        }

        // url编码函数
        static std::string urlEncode(const std::string &value)
        {
            std::ostringstream escaped;
            escaped.fill('0');
            escaped << std::hex;

            for (std::string::const_iterator i = value.begin(), n = value.end(); i != n; ++i)
            {
                std::string::value_type c = (*i);

                // Keep alphanumeric and other accepted characters intact
                if (isalnum(c) || c == '-' || c == '_' || c == '.' || c == '~')
                {
                    escaped << c;
                    continue;
                }

                // Any other characters are percent-encoded
                escaped << std::uppercase;
                escaped << '%' << std::setw(2) << int((unsigned char)c);
                escaped << std::nouppercase;
            }

            return escaped.str();
        }
        // URL解码函数
        static std::string urlDecode(const std::string &url)
        {
            std::string decoded;
            char hex[3] = {0};

            for (size_t i = 0; i < url.length(); ++i)
            {
                if (url[i] == '%')
                {
                    if (i + 2 < url.length())
                    {
                        hex[0] = url[++i];
                        hex[1] = url[++i];
                        decoded += static_cast<char>(std::stoi(hex, nullptr, 16));
                    }
                }
                else if (url[i] == '+')
                {
                    decoded += ' ';
                }
                else
                {
                    decoded += url[i];
                }
            }

            return decoded;
        }
        static bool checkEmail(char ch)
        {
            if (ch >= 'A' && ch <= 'Z')
                return true;
            else if (ch >= 'a' && ch <= 'z')
                return true;
            else if (ch >= '0' && ch <= '9')
                return true;
            else if (ch == '@' || ch == '.')
                return true;
            else
                return false;
        }
        static void checked(const httplib::Request &req, httplib::Response &rep)
        {
            // 获取邮箱地址
            size_t EmailPos = req.body.find("Email"); //@会被识别为%40,需要处理
            size_t EmailBegin = EmailPos + 6;
            // 检测邮箱是否正确
            if (req.body[EmailBegin] == '&') // Email为空的话,Email=&Password=22
            {
                rep.status = 301;
                std::string message = "邮箱不能为空";
                message = urlEncode(message);
                std::string url = "http://47.115.227.93:8080/LogIn.html?" + message;
                rep.set_header("Location", url.c_str());
                return;
            }

            size_t EmailEnd = req.body.find("%", EmailPos);

            size_t SuffixBegin = EmailEnd + 3;
            size_t suffixEnd = req.body.find("&", SuffixBegin);

            std::string Email = req.body.substr(EmailBegin, EmailEnd - EmailBegin) + '@' + req.body.substr(SuffixBegin, suffixEnd - SuffixBegin);

            // 获取密码
            size_t PasswordPos = req.body.find("Password");
            size_t PasswordBegin = PasswordPos + 9;
            size_t PasswordEnd = req.body.find("/r/n", PasswordBegin);
            std::string Password = req.body.substr(PasswordBegin, PasswordEnd - PasswordBegin);

            // 检验密码
            if (Password.empty())
            {
                rep.status = 301;
                std::string message = "密码不能为空";
                message = urlEncode(message);
                std::string url = "http://47.115.227.93:8080/LogIn.html?" + message;
                rep.set_header("Location", url.c_str());
                return;
            }

            // 查找有无账号
            User user;
            UserManager::GetInstance()->Find(Email, &user);
            if (user._Email != "")
            {
                if (user._password != Password)
                {
                    rep.status = 301;
                    std::string message = "账号或者密码错误";
                    message = urlEncode(message);
                    std::string url = "http://47.115.227.93:8080/LogIn.html?" + message;
                    rep.set_header("Location", url.c_str());

                    // rep.status = 201;
                    // std::string message = "该账号密码错误";
                    // rep.body += message;
                    rep.set_header("Content-Type", "text/html;charset=utf8");
                }
                else
                {
                    // 获取用户名
                    User use;
                    UserManager::GetInstance()->Find(Email, &use);
                    std::string name = use._name;

                    // 获取邮箱前两位
                    std::string num = Email.substr(0, 2);

                    rep.status = 301;
                    std::string message = name + "&" + num;
                    message = urlEncode(message);
                    std::string url = "http://47.115.227.93:8080/LogIn.html?" + message;
                    rep.set_header("Location", url.c_str());

                    // rep.status = 201;
                    // std::string message = "登录成功";
                    // rep.body += message;
                    // rep.set_header("Content-Type", "text/html;charset=utf8");
                }
            }
            else
            {
                rep.status = 301;
                std::string message = "账号还未注册";
                message = urlEncode(message);
                std::string url = "http://47.115.227.93:8080/LogIn.html?" + message;
                rep.set_header("Location", url.c_str());

                // rep.status = 201;
                // std::string message = "该账号还未注册";
                // rep.body += message;
                // rep.set_header("Content-Type", "text/html;charset=utf8"); // 设置编码格式为utf8,json序列化为unique,查看user.data的name为\u6212\u9152\u7684\u674e\u767d
            }
        }
        static void register_(const httplib::Request &req, httplib::Response &rep)
        {
            // User=2825280463&Email=2825280463%40qq.com&Password=2825280463zZ
            // 获取账号名字
            size_t UserPos = req.body.find("User");
            size_t UserBegin = UserPos + 5;
            size_t UserEnd = req.body.find("&", UserPos);
            std::string UserName = req.body.substr(UserBegin, UserEnd - UserBegin);
            UserName = urlDecode(UserName); // 将%E8%AF%A5%E8%B4%A6%E5%8F%B7%E8%BF%98%E6%9C%AA%E6%B3%A8%E5%86%8C转为汉字

            if (UserName.empty())
            {
                rep.status = 301;
                std::string message = "用户名不能为空";
                message = urlEncode(message);
                std::string url = "http://47.115.227.93:8080/LogIn.html?" + message;
                rep.set_header("Location", url.c_str());
                return;
            }

            // 判断用户名是否被使用过
            if (UserManager::GetInstance()->GetEmail(UserName) != "")
            {
                rep.status = 301;
                std::string message = "该用户名已经被使用过了";
                message = urlEncode(message);
                std::string url = "http://47.115.227.93:8080/LogIn.html?" + message;
                rep.set_header("Location", url.c_str());
                return;
            }

            // 获取邮箱地址
            size_t EmailPos = req.body.find("Email");
            size_t EmailBegin = EmailPos + 6;
            // 检测邮箱是否正确
            if (req.body[EmailBegin] == '&') // Email为空的话,Email=&Password=22
            {
                rep.status = 301;
                std::string message = "邮箱不能为空";
                message = urlEncode(message);
                std::string url = "http://47.115.227.93:8080/LogIn.html?" + message;
                rep.set_header("Location", url.c_str());
                return;
            }
            size_t EmailEnd = req.body.find("%", EmailPos);

            size_t SuffixBegin = EmailEnd + 3;
            size_t suffixEnd = req.body.find("&", SuffixBegin);

            std::string Email = req.body.substr(EmailBegin, EmailEnd - EmailBegin) + '@' + req.body.substr(SuffixBegin, suffixEnd - SuffixBegin);

            // 检测邮箱是否正确
            for (auto ch : Email)
            {
                if (checkEmail(ch) == false)
                {
                    rep.status = 301;
                    std::string message = "邮箱包含除英文和数字@.之外的字符";
                    message = urlEncode(message);
                    std::string url = "http://47.115.227.93:8080/LogIn.html?" + message;
                    rep.set_header("Location", url.c_str());
                    return;
                }
            }
            int pos = Email.find("@");
            std::string suffix = Email.substr(pos);
            std::string prefix = Email.substr(0, pos);
            if ((suffix != "@163.com" && suffix != "@126.com" && suffix != "@qq.com" && suffix != "@gmail.com" && suffix != "@sina.com" && suffix != "@139.com") || Email.size() > 320 || prefix.size() < 5 || prefix.size() > 12)
            {
                rep.status = 301;
                std::string message = "邮箱后缀或者邮箱个数不正确";
                message = urlEncode(message);
                std::string url = "http://47.115.227.93:8080/LogIn.html?" + message;
                rep.set_header("Location", url.c_str());
                return;
            }

            // 判断邮箱是否已经注册
            User use;
            bool IsRegister = UserManager::GetInstance()->Find(Email, &use);
            if (IsRegister)
            {
                rep.status = 301;
                std::string message = "该邮箱已经注册过了";
                message = urlEncode(message);
                std::string url = "http://47.115.227.93:8080/LogIn.html?" + message;
                rep.set_header("Location", url.c_str());
                return;
            }

            // 获取密码
            size_t PasswordPos = req.body.find("Password");
            size_t PasswordBegin = PasswordPos + 9;
            size_t PasswordEnd = req.body.find("/r/n", PasswordBegin);
            std::string Password = req.body.substr(PasswordBegin, PasswordEnd - PasswordBegin);

            if (Password.empty())
            {
                rep.status = 301;
                std::string message = "密码不能为空";
                message = urlEncode(message);
                std::string url = "http://47.115.227.93:8080/LogIn.html?" + message;
                rep.set_header("Location", url.c_str());
                return;
            }

            // 构建User对象
            User user;
            user._name = UserName;
            user._Email = Email;
            user._password = Password;
            UserManager::GetInstance()->AddUser(user);

            rep.status = 301;
            std::string message = "账号注册成功";
            message = urlEncode(message);
            std::string url = "http://47.115.227.93:8080/LogIn.html?" + message;
            rep.set_header("Location", url.c_str());
            rep.set_header("Content-Type", "text/html;charset=utf8");
        }

    public:
        service()
        {
            _server_ip = config::GetInstance()->GetServerIp();
            _server_port = config::GetInstance()->GetServerPort();
            _download_prefix = config::GetInstance()->GetDownloadPrefix();
        }
        bool RunModule()
        {
            _server.set_mount_point("/", "./Page_Image"); // 设置网页根目录
            _server.Post("/upload", Upload);
            _server.Get("/listshow", ListShow);
            _server.Get("/", HomePage);
            _server.Post("/checked", checked);
            _server.Post("/register", register_);
            _server.Get("/logout", Logout);
            std::string download_url = _download_prefix + "(.*)";
            _server.Get(download_url, Download);
            _server.listen(_server_ip, _server_port);
            return true;
        }
    };
}

#endif
